Skip to content

Starfield Button

Web designers and developers can learn a heck of a lot about UX from video games.

Starfield, published by Bethesda Game Studios, is an action role-playing game. You play a miner who gets caught up in an exploratory space adventure.

The buttons in this game caught my attention. Many of them are triggered by pressing and holding a specific key.

For example, to land on a planet, you need to press and hold the X key. If you quickly tap it, it doesn't work:

Similarly, to hail a ship, you need to press and hold E.

In this challenge, we're going to build a similar button. The user will need to press and hold a specific key to trigger the button.

Here's a quick demo showing what we're building, using the MacOS accessibility keyboard (so that you can see how long I'm holding the keys for):

Difficulty

This is a bit of a step up in difficulty. There's nothing too wild here, but it can be difficult figuring out exactly how this should work.

Rules

  • No additional third-party packages are allowed.
    • the clsx utility package has been included to make it easier to apply multiple CSS classes to an element.
  • Feel free to google anything you'd like.

Playground

Code Playground

/*
Acceptance Criteria:

• Pressing and holding the specified keyboard key for 1 second should “activate” the button, firing a callback that is specified by the parent.
• The button shouldn't have to be focused for this to work.
• Clicking/tapping the button should also activate the button. This is important, since not everyone will be able to press and hold the key (eg. touchscreen users).
• If the user releases the keyboard key before the full second has elapsed, the activation should be cancelled, and nothing should happen.
• The progress should be represented visually, by setting the `--progress` CSS variable on the `<button>`. This value should range from 0 (default) to 1 (fully activated).
• When the button is fully activated, you can add the `"confirmed"` CSS class to apply the “flash” animation, shown in the video above.
*/

import React from 'react';
import clsx from 'clsx';

import styles from './StarfieldButton.module.css';

function StarfieldButton({ letter }) {
return (
<button
// Add the "styles.confirmed" class to
// apply the confirmation flash animation:
className={styles.wrapper}
// This value controls the visual progress
// animation. Ranges from 0 to 1:
style={{ '--progress': 0.3 }}
>
<span className={styles.letter}>{letter}</span>
<span className={styles.underline} />
</button>
);
}

export default StarfieldButton;

My attempt

I mentioned towards the end of the video that you can continue to improve this solution if you'd like. Here are some “stretch goal” suggestions:

  • Implement the holdDuration prop I alluded to, so that different buttons can be held for different amounts of time.
  • Add sound effects! You can use my use-sound NPM package for this. You can find free sound effects on freesound.org.
    • To install extra dependencies for these challenges, you'll need to switch to CodeSandbox. I shared more info in the parent lesson.
  • It might be worth separating out the "press-and-hold" logic from this particular UI. Maybe try creating a custom hook that encapsulates this behaviour?